package com.xy.job.admin.core.schedule

import java.util.concurrent.DelayQueue

class TimeWheel(
    /**
     * 一个时间槽的刻度(单位: ms)
     */
    private var slotTick: Long,
    /**
     * 时间轮的时间槽个数
     */
    private var slotSize: Int,
    /**
     * 任务队列
     */
    private var delayQueue: DelayQueue<ScheduleTaskList>,
    /**
     * 当前时间
     */
    currentTime: Long = System.currentTimeMillis(),
) {
    /**
     * 当前时间(为了定位任务所在的时间槽，需要对时间向下取整到刻度的倍数)
     */
    private var currentProgress = adjustExpirationForSlot(currentTime)
        set(currentTime) {
            field = adjustExpirationForSlot(currentTime)
        }

    /**
     * 时间轮的时间跨度
     */
    private var interval: Long = slotTick * slotSize
    /**
     * 时间槽
     */
    private var scheduleTaskLists: Array<ScheduleTaskList> = Array(slotSize) { ScheduleTaskList() }
    /**
     * 上层时间轮
     */
    @Volatile
    private var overflowWheel: TimeWheel? = null

    private fun adjustExpirationForSlot(expiration: Long) = expiration - (expiration % slotTick)

    private fun getOverFlowWheel(): TimeWheel {
        if (overflowWheel == null) {
            synchronized(this) {
                if (overflowWheel == null) {
                    overflowWheel = TimeWheel(
                        slotTick = interval,
                        slotSize = slotSize,
                        delayQueue = delayQueue,
                        currentTime = currentProgress,
                    )
                }
            }
        }
        return overflowWheel!!
    }

    fun addTask(scheduleTask: ScheduleTask): Boolean {
        val expiration: Long = scheduleTask.expiration
        when {
            // 过期任务直接执行
            expiration < currentProgress + slotTick -> {
                return false
            }
            // 下个周期的任务加入到队列中
            expiration < currentProgress + interval -> {
                val slotIndex = (expiration / slotTick) % slotSize
                val timerTaskList = scheduleTaskLists[slotIndex.toInt()]
                timerTaskList.addTask(scheduleTask)
                // 如果时间槽中的任务没还有加入过任务队列，则将其加入任务队列中
                val adaptExpiration = adjustExpirationForSlot(expiration)
                if (timerTaskList.compareAndSetExpiration(adaptExpiration)) {
                    delayQueue.offer(timerTaskList)
                }
            }
            // 更后面的任务交给上层范围更大的时间轮调度
            else -> {
                val timeWheel = getOverFlowWheel()
                timeWheel.addTask(scheduleTask)
            }
        }
        return true
    }

    fun advanceTo(timestamp: Long) {
        if (timestamp >= currentProgress + slotTick) {
            currentProgress = timestamp
            overflowWheel?.advanceTo(timestamp)
        }
    }
}